// Copyright 2010 Keir Mierle. All rights reserved.

#include <iostream>
#include <gtest/gtest.h>

#include "using.h"
#include "gomoku.h"

TEST(BitTwiddles, LowestBitPosition) {
  EXPECT_EQ(0,  LowestBitPosition(1));
  EXPECT_EQ(1,  LowestBitPosition(2));
  EXPECT_EQ(2,  LowestBitPosition(4));
  EXPECT_EQ(3,  LowestBitPosition(8));
}

TEST(BitTwiddles, BitPositionFunctionsAreInvertible) {
  for (int i = 0; i < 8; ++i) {
    for (int j = 0; j < 8; ++j) {
      uint64 bit = BitForPosition(i, j);
      int ii, jj;
      PositionForBit(bit, &ii, &jj);
      EXPECT_EQ(i, ii) << "j=" << j;
      EXPECT_EQ(j, jj) << "i=" << i;
    }
  }
}

TEST(GomokuBoard, ToAndFromString) {
  const char *str_board = 
    ". . . . . . . . \n"
    ". . . . . . . . \n"
    ". o o o . . . . \n"
    ". . o . x . . . \n"
    ". . . x . . . . \n"
    ". . x . . . . . \n"
    ". . . . . . . . \n"
    ". . . . . . . . \n";

  GomokuBoard board;
  board.FromString(str_board);
  string from_str = board.ToString();
  EXPECT_EQ(string(str_board), from_str);
  cout << board.ToString();
}

enum TerminalState {
  WHITE_WINS,
  BLACK_WINS,
  NONTERMINAL
};

void ExpectTerminal(TerminalState expected_state, const char *str_board) {
  GomokuBoard board;
  board.FromString(str_board);
  cout << "Checking:\n" << board.ToString();
  if (expected_state == NONTERMINAL) {
    EXPECT_FALSE(board.IsTerminal());
    EXPECT_FALSE(Wins(board.black));
    EXPECT_FALSE(Wins(board.white));
  } else if (expected_state == WHITE_WINS) {
    EXPECT_TRUE(board.IsTerminal());
    EXPECT_TRUE(Wins(board.white));
    EXPECT_FALSE(Wins(board.black));
  } else if (expected_state == BLACK_WINS) {
    EXPECT_TRUE(board.IsTerminal());
    EXPECT_FALSE(Wins(board.white));
    EXPECT_TRUE(Wins(board.black));
  }
}

TEST(GomokuBoard, TerminalBoardsAreDetectedCorrectly) {
  struct {
    TerminalState expected_state;
    const char *board;
  } kTerminalCases[] = {
    { NONTERMINAL,
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". o o o . . . . \n"
      ". . o . x . . . \n"
      ". . . x . . . . \n"
      ". . x . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
    },
    { NONTERMINAL,
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". o o o . o . . \n"
      ". . o . x . . . \n"
      ". . . x . . . . \n"
      ". . x . . . . . \n"
      ". . . x . . . . \n"
      ". . . . . . . . \n"
    },
    { NONTERMINAL,  // Lots of long strings.
      "o o o o x o o o \n"
      ". o . . . . . o \n"
      ". . o . . o . o \n"
      ". . . o x . . o \n"
      ". x x x x o x x \n"
      ". . x . . o . o \n"
      ". . . x . . o o \n"
      ". . o . o o o o \n"
    },
    { BLACK_WINS,
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". o o o x o . . \n"
      ". . o . x . . . \n"
      ". . . x x . . . \n"
      ". . x . x . . . \n"
      ". . . x x . . . \n"
      ". . . . . . . . \n"
    },
    { WHITE_WINS,
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". o o o o o . . \n"
      ". . o . x . . . \n"
      ". . . x x . . . \n"
      ". . x . x . . . \n"
      ". . . x x . . . \n"
      ". . . . . . . . \n"
    },
    // Diagonals are trickier.
    { BLACK_WINS, 
      ". . . . . . . . \n"
      ". . . . . . x . \n"
      ". o o . o x . . \n"
      ". . o . x . . . \n"
      ". . . x x . . . \n"
      ". . x . x . . . \n"
      ". . . x x . . . \n"
      ". . . . . . . . \n"
    },
    { WHITE_WINS, 
      ". . . . . . . . \n"
      ". . . . . . x . \n"
      ". o o . o x . . \n"
      ". . o . x . . . \n"
      ". . . o x . . . \n"
      ". . x . o . . . \n"
      ". . . x x o . . \n"
      ". . . . . . . . \n"
    },
    { WHITE_WINS, 
      ". . . o . . . . \n"
      ". . . . o . . . \n"
      ". . . . . o . . \n"
      ". . . . . . o . \n"
      ". . . . . . . o \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
    },
    { WHITE_WINS, 
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      "o . . . . . . . \n"
      ". o . . . . . . \n"
      ". . o . . . . . \n"
      ". . . o . . . . \n"
      ". . . . o . . . \n"
    },
    { WHITE_WINS, 
      ". . . . o . . . \n"
      ". . . o . . . . \n"
      ". . o . . . . . \n"
      ". o . . . . . . \n"
      "o . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
    },
    { WHITE_WINS, 
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . o \n"
      ". . . . . . o . \n"
      ". . . . . o . . \n"
      ". . . . o . . . \n"
      ". . . o . . . . \n"
    },
    { WHITE_WINS, 
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . o . . . \n"
      ". . . o . . . . \n"
      ". . o . . . . . \n"
      ". o . . . . . . \n"
      "o . . . . . . . \n"
    },
    { WHITE_WINS, 
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . . . . . . \n"
      ". . . o . . . . \n"
      ". . . . o . . . \n"
      ". . . . . o . . \n"
      ". . . . . . o . \n"
      ". . . . . . . o \n"
    },
    { NONTERMINAL, NULL } // Terminator
  };
  for (int i = 0; kTerminalCases[i].board; ++i) {
    ExpectTerminal(kTerminalCases[i].expected_state, 
                   kTerminalCases[i].board); 
  }
}
